/*!***************************************************
 * @file: cbuf.c
 * @brief: 环形缓冲区
 * @author: ITHAO 
 * @date: 2018.11.23
 * @note: 
 ****************************************************/
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include "cbuf.h"

static bool is_power_of_2(uint32 n);
static uint32 roundup_power_of_2(uint32 a);
static struct cbuf *cbuf_init(uint8 *buffer, uint32 size);



static struct cbuf *cbuf_init(uint8 *buffer, uint32 size)
{
    struct cbuf *p_cbuf;
    int ret = 0;
 
    p_cbuf = (struct cbuf *)malloc(sizeof(struct cbuf));
    if (!p_cbuf)
        return NULL;

    p_cbuf->buffer = buffer;
    p_cbuf->size = size;
    p_cbuf->in = 0;
    p_cbuf->out = 0;

    ret = pthread_mutex_init(&p_cbuf->mutex, NULL);
    if (ret != 0) {
        free(p_cbuf);
        return NULL;
    }

    sem_init(&p_cbuf->sem, 0, 0);

    return p_cbuf;
}

struct cbuf *cbuf_alloc(uint32 size)
{
    uint8 *buffer;
    struct cbuf *ret;
 /*
 * round up to the next power of 2, since our 'let the indices
 * wrap' tachnique works only in this case.
 */
    if(!is_power_of_2(size))
        size = roundup_power_of_2(size);
 
    buffer = (uint8 *)malloc(size);
    if (!buffer)
        return NULL;
 
    ret = cbuf_init(buffer, size);
    if (!ret)
        free(buffer);
 
    return ret;
}

void cbuf_free(struct cbuf *p_cbuf) 
{
    if (p_cbuf) {
        if (p_cbuf->buffer)
            free(p_cbuf->buffer);
        free(p_cbuf);
    }
}

/*这一般被接收线程调用*/
uint32 cbuf_put(struct cbuf *p_cbuf, uint8 *buffer, uint32 len)
{
    uint32 l = 0;
 
    /*写入长度与缓冲区剩余长度比较取最少者*/
   // len = min(len, p_cbuf->size - p_cbuf->in + p_cbuf->out);
    if (len > (p_cbuf->size - p_cbuf->in + p_cbuf->out))
        return 0;
        
    /*in到buffer尾的长度与输入长度比较取最小者*/
    l = min(len, p_cbuf->size - (p_cbuf->in & (p_cbuf->size -1)));

    pthread_mutex_lock(&p_cbuf->mutex);
    /*先把数据写道in到末尾*/
    memcpy(p_cbuf->buffer + (p_cbuf->in & (p_cbuf->size -1)), buffer, l);
     /*再将剩余的冲buffer头写道in*/
    memcpy(p_cbuf->buffer, buffer + l, len - l);
    /*直接加，溢出之后自动重头开始*/
    p_cbuf->in += len;
    pthread_mutex_unlock(&p_cbuf->mutex);
    
    /*有数据放入缓冲区，信号量+1*/
    sem_post(&p_cbuf->sem);

    return len;
}

/*这里一般被工作线程调用*/
uint32 cbuf_get(struct cbuf *p_cbuf, uint8 *buffer, uint32 len)
{
    uint32 l;

    /*如果没数据则阻塞在这里*/
    sem_wait(&p_cbuf->sem);

    /* 这里与写入同理，就不重复了*/
    len = min(len, p_cbuf->in - p_cbuf->out); 
 
    l = min(len, p_cbuf->size - (p_cbuf->out & (p_cbuf->size -1)));

    pthread_mutex_lock(&p_cbuf->mutex);
    memcpy(buffer, p_cbuf->buffer + (p_cbuf->out & (p_cbuf->size -1)), l);

    memcpy(buffer + l, p_cbuf->buffer, len - l);

    p_cbuf->out += len;
    pthread_mutex_unlock(&p_cbuf->mutex);
 
    
    
    return len;
}


/*判断n是否为2的幂*/
static bool is_power_of_2(uint32 n)
{
    return (n != 0 && ((n & (n - 1)) == 0));
}

/*将数字a向上取整为2的次幂*/
static uint32 roundup_power_of_2(uint32 a)
{
    int count = 0;
    uint32 position = 0;

    if (a == 0)
        return 0;

    for (count = a; count != 0; count >>= 1)
        position++;

    return (uint32)(1 << position);
}

